/**
 * 
 */
package com.netscenery.yang.model;

import java.math.BigInteger;
import java.util.ArrayList;

import org.jdom2.Attribute;
import org.jdom2.Element;

import com.netscenery.util.ErrTag;
import com.netscenery.util.Link;
import com.netscenery.util.Link.SortOrder;
import com.netscenery.util.Unicode;
import com.netscenery.util.XMLTag;
import com.netscenery.yang.YangKeyword;
import com.netscenery.yang.YangStatement;
import com.netscenery.yang.designer.MapInfo;
import com.netscenery.yang.parser.YangParserException;

/**
 * @author llly
 *
 */
public class Range extends ModelElement{
	private Link<RangeEx> ranges;
	private String description;
	private String reference;
	private BigInteger lowBound = RangeEx.INT32_LOW_BOUND;
	private BigInteger highBound = RangeEx.INT32_HIGH_BOUND;;
	
	public Range(){
		super();
	}
	
	public Range(BigInteger lowBound, BigInteger highBound){
		this.lowBound = lowBound;
		this.highBound = highBound;
	}
	
	public RangeEx getNextRange(int fromIndex){
		if (null == ranges){
			return null;
		}
		
		RangeEx ex = ranges.getNext(fromIndex);
		if (null == ex){
			return null;
		}
		return ex;
	}

	public RangeEx createRange(){
		return new RangeEx(this.getLowBound(), this.getHighBound());
	}
	
	public RangeEx createRange(String min,String max){
		RangeEx range= createRange();
		if (null != min){
			range.setMin(min);
		}
		if (null != max){
			range.setMax(max);
		}
		return range;
	}
	
	public boolean addRange(RangeEx range){
		if (null == range){
			return false;
		}
		int rangesSize = 0;

		//首先要检查待加入的range，其上下限是否和ranges一致。
		if ((this.lowBound != range.lowBound )
			||(this.highBound != range.highBound)){
			return false;
		}

		//新加入的 range不能和现有的range有重合
		if (null != ranges){
			rangesSize = ranges.size();
			for (int i = 0; i < rangesSize; i++) {
				RangeEx rangeEx = ranges.get(i);
				if (null != rangeEx) {
					if (true == rangeEx.hasInterSection(range)) {
						return false;
					}
				}
			}
		}
		
		//需要从小到大进行排序
		if (null == ranges){
			ranges = new Link<RangeEx>(SortOrder.ASCEND);
		}
		ranges.insert(range);
		return true;

	}
	
	public RangeEx getRange(int index){
		if (null == ranges){
			return null;
		}
		
		int size = ranges.size();
		if (index >= size){
			return null;
		}
		
		return ranges.get(index);
	}
	

	public int getRangeIndex(RangeEx range){
		if (null == ranges){
			return -1;
		}
		
		return ranges.getIndex(range);
		
	}
	
	public RangeEx removeRange(int index){
		if (null == ranges){
			return null;
		}
		
		return ranges.remove(index);
		
		
	}
	
	public RangeEx removeRange(RangeEx range){
		if (null == ranges){
			return null;
		}
		
		int index = getRangeIndex(range);
		if (-1 == index){
			return null;
		}
		
		return ranges.remove(index);
		
		
	}

	public int getRangesSize(){
		if (null == ranges){
			return 0;
		}
		
		return ranges.size();
	}
	/**
	 * @return the description
	 */
	public String getDescription() {
		return description;
	}
	/**
	 * @param description the description to set
	 */
	public void setDescription(String description) {
		this.description = description;
	}
	/**
	 * @return the reference
	 */
	public String getReference() {
		return reference;
	}
	/**
	 * @param reference the reference to set
	 */
	public void setReference(String reference) {
		this.reference = reference;
	}
	
	/**
	 * @return the lowBound
	 */
	public BigInteger getLowBound() {
		return lowBound;
	}


	/**
	 * @param lowBound the lowBound to set
	 */
	public void setLowBound(BigInteger lowBound) {
		this.lowBound = lowBound;
	}


	/**
	 * @return the highBound
	 */
	public BigInteger getHighBound() {
		return highBound;
	}


	/**
	 * @param highBound the highBound to set
	 */
	public void setHighBound(BigInteger highBound) {
		this.highBound = highBound;
	}
	/**
	 * ���range�Ƿ�����������
	 * @return
	 */
	public boolean checkRangeOrder(){
		int size = this.getRangesSize();
		if (0 == size){
			return false;
		}
		
		for (int i = 0; i < size; i++){
			RangeEx pre= getRange(i);
			if (null == pre){
				continue;
			}
			if (i +1 < size){
				RangeEx next = getRange(i+1);
				if (new BigInteger(next.getMin()).compareTo(new BigInteger(pre.getMax())) < 0){
					return false;
				}
			}
		}
		
		return true;
	}

	public String getRangeExpression(){
		StringBuffer sb = null;
		if (null == this.ranges){
			return null;
		}
		
		int size = this.getRangesSize();
		
		for (int i = 0; i < size; i ++){
			RangeEx ran = this.getRange(i);
			if (null == ran){
				continue;
			}
			
			if (null == sb){
				sb = new StringBuffer(ran.getExpression());
			}
			else {
				sb.append(" | ");
				sb.append(ran.getExpression());
			}
		}
		
		return sb.toString();
	}
	
	public boolean parseRangeExpression(String expression){
		if (null == expression){
			return false;
		}
		
		if (!expression.contains("|")){
			RangeEx ran = new RangeEx(this.lowBound,this.highBound);
			ran.parseExpression(expression);
			this.addRange(ran);
			return true;
		}
		
		String[] strs = expression.split("\\|");
		if (null == strs){
			return false;
		}
		
		int size = strs.length;
		for(int i = 0; i < size; i++){
			if (null != strs[i]){
				RangeEx ran = new RangeEx(this.lowBound,this.highBound);
				ran.parseExpression(strs[i].trim());
				this.addRange(ran);
			}
		}
		
		return true;
	}
	/**
	 * �ж�����ĵ��η�Χ�Ƿ��ڱ�range��Χ��
	 * @param rangeEx
	 * @return
	 */
	public boolean isInRange(RangeEx rangeEx){
		if (null == rangeEx){
			return false;
		}
		
		if (rangeEx.highBound.compareTo(this.highBound) > 0){
			return false;
		}
		
		if (rangeEx.lowBound.compareTo(this.lowBound) < 0){
			return false;
		}
		if (null == this.ranges){
			return true;
		}
		int size = this.ranges.size();
		
		for(int i = 0; i < size; i++){
			RangeEx ex = this.getRange(i);
			if (null != ex){
				if (ex.isInRangeEx(rangeEx)){
					return true;
				}
			}
		}
		
		return false;
	}
	
	/**
	 * �ж������range�Ƿ��ڱ�range�ķ�Χ��
	 * @param range
	 * @return
	 */
	public boolean isInRange(Range range){
		if (range.getHighBound().compareTo(this.getHighBound()) > 0 ){
			return false;
		}
		
		if (range.getLowBound().compareTo(this.getLowBound()) < 0) {
			return false;
		}
		
		int size = range.getRangesSize();
		for (int i = 0; i < size; i++){
			RangeEx ex = range.getRange(i);
			if (null != ex){
				if (!this.isInRange(ex)){
					return false;
				}
			}
		}
		return true;
		
	}
	
	public boolean isInRange(BigInteger bi){

		if (bi.compareTo(this.getHighBound()) > 0){
			return false;
		}
		
		if (bi.compareTo(this.getLowBound()) < 0){
			return false;
		}
		
		if ((null == this.ranges) 
				|| (this.ranges.size() == 0)){
			return true;
		}
		
		int size = this.getRangesSize();
		
		for (int i =0 ; i < size; i++ ){
			RangeEx ex = this.getRange(i);
			if (null != ex){
				if (ex.isInRangeEx(bi)){
					return true;
				}
			}
		}
		
		return false;
	}

	@Override
	public Validator validate() {
		Validator validator = new Validator();
		if (0 == this.getRangesSize()){
			validator.setIsValid(false);
			validator.addMsg(new Message(this, ErrTag.ERROR,"invalid range/length.It must at least have one rang/length value ."));
		}
		else {
			if(!checkRangeOrder()){
				validator.setIsValid(false);
				validator.addMsg(new Message(this, ErrTag.ERROR,"invalid range/length.It must be ascending order."));
			}
			
		}
		if (!isNullString(this.description)){
			if (Unicode.containChinese(this.description)){
				validator.setIsValid(false);
				validator.addMsg(new Message(this,ErrTag.ERROR,"description",ErrorCode.INVALID_CHARACTER));
			}
		}
		
		if (!isNullString(this.reference)){
			if (Unicode.containChinese(this.reference)){
				validator.setIsValid(false);
				validator.addMsg(new Message(this,ErrTag.ERROR,"reference",ErrorCode.INVALID_CHARACTER));
			}
		}
		Validator sValidator = super.validate();
		validator.copy(sValidator);
		return validator;
	}

	@Override
	public YangStatement toYang() {
		if (null == env){
			return null;
		}
		YangStatement statement = null;
		if (env.getParent() instanceof YangString
				|| env.getParent() instanceof Binary){
			statement = new YangStatement(YangStatement.YANG_STATEMENT_LENGTH,this.getRangeExpression());
		}
		else {
			statement = new YangStatement(YangStatement.YANG_STATEMENT_RANGE,this.getRangeExpression());
		}
		
		if (null != this.getDescription()){
			YangStatement descr = new YangStatement(YangStatement.YANG_STATEMENT_DESCRIPTION,this.getDescription());
			statement.addChild(descr);
		}
		
		if (null != this.getReference()){
			YangStatement ref = new YangStatement(YangStatement.YANG_STATEMENT_REFERENCE,this.getReference());
			statement.addChild(ref);
		}
		
		return statement;
	}

	@Override
	public Element toXML() {
		if (null == env){
			return null;
		}
		Element element = null;
		if (env.getParent() instanceof YangString
				|| env.getParent() instanceof Binary){
			element = new Element(XMLTag.XML_TAG_LENGTH);
		}
		else {
			element = new Element(XMLTag.XML_TAG_RANGE);
		}
		
		if (null != this.ranges){
			int size = this.ranges.size();
			for (int i = 0; i < size; i ++){
				RangeEx ex = ranges.get(i);
				if (!ex.getMin().equals(ex.getMax())){
					Element min = new Element(XMLTag.XML_TAG_MIN);
					min.setText(ex.getMinEx());
					Element max = new Element(XMLTag.XML_TAG_MAX);
					max.setText(ex.getMaxEx());				
					element.addContent(min);
					element.addContent(max);
				}			
				else {
					Element value = new Element(XMLTag.XML_TAG_VALUE);
					value.setText(ex.getMinEx());
					element.addContent(value);
				}
			}
			
		}
		
		if (null != this.getDescription()){
			Element descr = new Element(XMLTag.XML_TAG_DESCRIPTION);
			descr.setText(this.getDescription());
			element.addContent(descr);
		}
		
		if (null != this.getReference()){
			Element ref = new Element(XMLTag.XML_TAG_REFERENCE);
			ref.setText(this.getReference());
			element.addContent(ref);
		}
		
		return element;
	}

	@Override
	public boolean isFieldMandatory(String field) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public int readXML(Element element) {
		if (null == element){
			return MODELELEMENT_OK;
		}
		
		String value = element.getText();
		if (element.getName().equals(XMLTag.XML_TAG_DESCRIPTION)){
			this.description = value;
		}
		else if (element.getName().equals(XMLTag.XML_TAG_REFERENCE)){
			this.reference = value;
		}
		else if (element.getName().equals(XMLTag.XML_TAG_MIN)
				|| element.getName().equals(XMLTag.XML_TAG_MAX)
				|| element.getName().equals(XMLTag.XML_TAG_VALUE)){
			return MODELELEMENT_OK;
		}
		else {
			return MODELELEMENT_FAIL;
		}
		
		return MODELELEMENT_OK;
	}

	@Override
	public int readYANG(YangStatement statement) throws YangParserException {
		if (null == statement){
			return MODELELEMENT_OK;
		}
		String value = statement.getArgument();
		if (statement.getKeyword().equals(YangStatement.YANG_STATEMENT_DESCRIPTION)){
			this.description = value;
		}
		else if (statement.getKeyword().equals(YangStatement.YANG_STATEMENT_REFERENCE)){
			this.reference = value;
		}
		else {
			return MODELELEMENT_FAIL;
		}
		
		return MODELELEMENT_OK;
	}

	public  MapInfo getMapInfo() throws SecurityException, NoSuchMethodException {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Element toYin() {
		if (null == env){
			return null;
		}
		Element statement = null;
		if (env.getParent() instanceof YangString
				|| env.getParent() instanceof Binary){
			statement = new Element(YangStatement.YANG_STATEMENT_LENGTH,env.getNs());
		}
		else {
			statement = new Element(YangStatement.YANG_STATEMENT_RANGE,env.getNs());
			
		}
		Attribute value = new Attribute(YangKeyword.VALUE.getKeyword(),this.getRangeExpression());
		statement.setAttribute(value);
		if (!isNullString(this.getDescription())){
			Element descr = new Element(YangStatement.YANG_STATEMENT_DESCRIPTION,env.getNs());
			Element text = new Element(XMLTag.XML_TAG_TEXT,env.getNs());
			text.setText(this.description);
			descr.addContent(text);
			statement.addContent(descr);
			
		}
		
		if (!isNullString(this.getReference())){
			Element ref = new Element(YangStatement.YANG_STATEMENT_REFERENCE,env.getNs());
			Element text = new Element(XMLTag.XML_TAG_TEXT,env.getNs());
			text.setText(this.reference);
			ref.addContent(text);
			statement.addContent(ref);
		}
		
		return statement;
	}

	@Override
	public int readYin(Element statement) throws ModelException {
		if (null == statement){
			return MODELELEMENT_OK;
		}
		// ����
		if (statement.getName().equals(YangStatement.YANG_STATEMENT_DESCRIPTION)) {
			Element text = statement.getChild(XMLTag.XML_TAG_TEXT,env.getNs());
			if (null == text){
				return MODELELEMENT_FAIL;
			}
			this.setDescription(text.getText());
		}
		// reference
		else if (statement.getName().equals(
				YangStatement.YANG_STATEMENT_REFERENCE)) {
			Element text = statement.getChild(XMLTag.XML_TAG_TEXT,env.getNs());
			if (null == text){
				return MODELELEMENT_FAIL;
			}
			this.setReference(text.getText());
		}
		else {
			return MODELELEMENT_FAIL;
		}
		
		return MODELELEMENT_OK;
	}

	@Override
	public void update() {
		// TODO Auto-generated method stub
		
	}

}
